﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace ShellLib
{
    /// <summary>
    /// Deals with shell functions realated to icons. Specifically, file icons.
    /// </summary>
    public static class IconFuncs
    {

        private const uint SHGFI_ICON = 0x100;
        private const uint SHGFI_USEFILEATTRIBUTES = 0x10;

        #region IconSize enum

        /// <summary>
        /// Represents standard icon size constants
        /// </summary>
        public enum IconSize : uint
        {
            /// <summary>
            /// 32x32
            /// </summary>
            Large = 0x0,
            /// <summary>
            /// 16x16
            /// </summary>
            Small = 0x1
        }

        #endregion



        /// <summary>
        /// Extracts all icons of specified size from file 
        /// </summary>
        /// <param name="filename">Path of file to extract icons from</param>
        /// <param name="size">Large or Small icon</param>
        /// <returns>An array of Icons</returns>
        public static Icon[] IconsFromFile(string filename, IconSize size)
        {
            int iconCount = Imports.ExtractIconEx(filename, -1, null, null, 0); //checks how many icons. 
            var iconPtr = new IntPtr[iconCount];
            var iconList = new Icon[iconCount];

            //extracts the icons by the size that was selected. 
            if (size == IconSize.Small)
            {
                Imports.ExtractIconEx(filename, 0, null, iconPtr, iconCount);
            }
            else
            {
                Imports.ExtractIconEx(filename, 0, iconPtr, null, iconCount);
            }

            //gets the icons in a list. 
            for (int i = 0; i < iconCount; i++)
            {
                iconList[i] = (Icon)Icon.FromHandle(iconPtr[i]).Clone();
                Imports.DestroyIcon(iconPtr[i]);
            }

            return iconList;
        }

        /// <summary>
        /// extract one selected by index icon from a file.
        /// </summary>
        /// <param name="filename">Path of file to extract icons from</param>
        /// <param name="size">Large or Small icon</param>
        /// <param name="index">Index of icon to grab</param>
        /// <returns>The extracted icon</returns>
        public static Icon IconFromFile(string filename, IconSize size, int index)
        {
            int iconCount = Imports.ExtractIconEx(filename, -1,
                                          null, null, 0); //checks how many icons. 
            if (iconCount < index) return null; // no icons was found. 

            var iconPtr = new IntPtr[1];

            //extracts the icon that we want in the selected size. 
            if (size == IconSize.Small)
            {
                Imports.ExtractIconEx(filename, index, null, iconPtr, 1);
            }
            else
            {
                Imports.ExtractIconEx(filename, index, iconPtr, null, 1);
            }

            return GetManagedIcon(iconPtr[0]);
        }

        /// <summary>
        /// this will look through the registry to find if the passed extension has an icon. 
        /// </summary>
        /// <param name="extension">File extension</param>
        /// <param name="size">Large or Small icon</param>
        /// <returns>Icon associated with the file type</returns>
        public static Icon IconFromExtension(string extension, IconSize size)
        {
            try
            {
                //add '.' if necessary. Easier to call this funtion using Path.GetExtension()
                if (extension[0] != '.') extension = '.' + extension;

                //opens the registry for the wanted key. 
                var root = Registry.ClassesRoot;
                var extensionKey = root.OpenSubKey(extension);

                if (extensionKey == null)
                {
                     return null;
                }
                extensionKey.GetValueNames();
                var applicationKey = root.OpenSubKey(extensionKey.GetValue("").ToString());

                if (applicationKey == null)
                {
                    return null;
                }

                //gets the name of the file that have the icon. 
                var iconLocation = applicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString();
                string[] iconPath = iconLocation.Split(',');

                if (iconPath[1] == null) iconPath[1] = "0";
                IntPtr[] large = new IntPtr[1], small = new IntPtr[1];

                //extracts the icon from the file. 
                Imports.ExtractIconEx(iconPath[0], Convert.ToInt16(iconPath[1]), large, small, 1);

                return GetManagedIcon(size == IconSize.Large ? large[0] : small[0]);
            }
            catch (Exception e)
            {
                Debug.WriteLine("error while trying to get icon for " + extension + " :" + e.Message);
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="extension"></param>
        /// <param name="size">Large or Small icon</param>
        /// <returns></returns>
        public static Icon IconFromExtensionShell(string extension, IconSize size)
        {
            try
            {
                //add '.' if nessesry 
                if (extension[0] != '.')
                {
                    extension = '.' + extension;
                }

                //temp struct for getting file shell info 
                var tempFileInfo = new SHFILEINFO();
                Imports.SHGetFileInfo(
                    extension,
                    0,
                    ref tempFileInfo,
                    (uint)Marshal.SizeOf(tempFileInfo),
                    SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | (uint)size);

                return GetManagedIcon(tempFileInfo.hIcon);
            }
            catch (Exception e)
            {
                Debug.WriteLine("error while trying to get icon for " + extension + " :" + e.Message);
                return null;
            }
        }

        /// <summary>
        /// Extracts an icon from an assemblies resource file
        /// </summary>
        /// <param name="resourceName">path to resource file</param>
        /// <returns>Icon</returns>
        public static Icon IconFromResource(string resourceName)
        {
            var tempAssembly = Assembly.GetCallingAssembly();

            if (tempAssembly != null)
            {
                return new Icon(tempAssembly.GetManifestResourceStream(resourceName));
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceImage"></param>
        /// <param name="iconFilename"></param>
        /// <param name="destinationIconSize"></param>
        public static void SaveIconFromImage(Image sourceImage, string iconFilename, IconSize destinationIconSize)
        {
            var newIconSize = destinationIconSize == IconSize.Large ? new Size(32, 32) : new Size(16, 16);

            var rawImage = new Bitmap(sourceImage, newIconSize);
            var tempIcon = Icon.FromHandle(rawImage.GetHicon());
            var newIconStream = new FileStream(iconFilename, FileMode.Create);

            tempIcon.Save(newIconStream);

            newIconStream.Close();
        }

        private static Icon GetManagedIcon(IntPtr hIcon)
        {
            return Icon.FromHandle(hIcon);
        }
    }
}